package comm;

import java.io.FileInputStream;
import java.io.IOException;
import java.net.SocketTimeoutException;
import java.security.KeyStore;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.TrustManagerFactory;

// 监听线程
class ListenThread extends Thread {
	private boolean isEnd;
	private final int SERVER_PORT;
	private final int SERVER_ACCEPT_TIMEOUT_TIME = 1 * 1000; // server的accept超时时间（mS）
	private SSLServerSocket serversocket;
	private TimeOutTimer timeOutTimer = new TimeOutTimer(60 * 1000); // 定时刷新，供主循环线程监视线程状态
	
	private Log log = new Log();

	private IONodeList.DoingForListenThread doingForListenThread;

	ListenThread(IONodeList.DoingForListenThread aDoingForListenThread) {
		this.setName(this.getClass().getName());
		isEnd = false;
		int num = Integer.parseInt(PropertyManager.getProtery(
				"CommServerListenPort", "6666"));
		if(num < 0) num = 6666;
		this.SERVER_PORT = num;
		this.doingForListenThread = aDoingForListenThread;
	}

	Log getLog() {
		return this.log;
	}

	boolean isEnd() {
		return this.isEnd;
	}
	
	// 主循环线程调用，检查监听线程是否卡住
	boolean isTimeOut() {
		return this.timeOutTimer.getTimeOutState();
	}

	// 终止线程
	// // 注：调用此函数时也许run()的代码还没有开始执行
	// // 注：可以硬关闭，没有善后工作
	void end() {
		this.isEnd = true;
		try {
			if (null != this.serversocket) {
				this.serversocket.close();
				this.serversocket = null;
			}
		} catch (IOException e) {
			log.print(Log.warn, e.toString());
			Log.println(e.toString());
		}
	}

	public void run() {
		try {
			log.print(Log.info, "ClientListenThread Start:" + this.SERVER_PORT);
			try {
				serversocket = getServerSocket(SERVER_PORT);
			} catch (Exception e) {
				log.print(Log.error, e.toString());
				log.print(Log.error, "New SSLSocket failing");
				this.end();
			}

			SSLSocket socket = null;
			IONode ionode = null;
			while (false == this.isEnd()) {
				
				this.timeOutTimer.resetTimer();
				
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					Log.println(e.toString());
				}

				// accept
				try {
					socket = (SSLSocket) serversocket.accept();
				} catch (SocketTimeoutException e) {
					continue;
				} catch (IOException e) {
					log.print(Log.error, e.toString());
					break;
				}

				log.print(Log.info, "New Connect From" + socket.getInetAddress() + ":"
						+ socket.getPort());
				try {
					try {
						socket.startHandshake();
					} catch (SSLHandshakeException e) {
						log.print(Log.warn, e.toString());
						log.print(Log.warn,
								"SSL Handshake failing, close socket" + socket.getInetAddress() + ":"
										+ socket.getPort());
						socket.close();
						socket = null;
						continue;
					}
					ionode = new IONode(socket);
					if (true == this.doingForListenThread.addUninitNode(ionode)) {
						ionode.start();
					} else {
						log.print(Log.warn, "Add node failing");
						// 关闭节点
						ionode = null;
						socket.close();
						socket = null;
						continue;
					}
				} catch (IOException e) {
					log.print(Log.warn, e.toString());
					if (null != socket) {
						try {
							socket.close();
						} catch (IOException e1) {
							log.print(Log.warn, e1.toString());
						}
						socket = null;
					}
				}
			}
		} catch (Exception e) {
			Log.println(e);
			log.print(Log.error, e.toString());
		} finally {
			this.end();
			log.print(Log.info, "ClientListenThread Stop");
		}

	}

	// 获取SSLServersocket
	private SSLServerSocket getServerSocket(int thePort) throws Exception {
		
		String key1 = PropertyManager.getProtery("KeyServer", "./kserver.keystore");
		String key2 = PropertyManager.getProtery("KeyClient", "./tserver.keystore");

		char keyStorePass[] = "123456".toCharArray();
		char keyPassword[] = "123456".toCharArray();

		KeyStore ks = KeyStore.getInstance("JKS");
		KeyStore tks = KeyStore.getInstance("JKS");

		ks.load(new FileInputStream(key1), keyStorePass);
		tks.load(new FileInputStream(key2), keyStorePass);

		KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
		TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");

		kmf.init(ks, keyPassword);
		tmf.init(tks);

		SSLContext sslContext = SSLContext.getInstance("SSLv3");
		sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);

		SSLServerSocketFactory factory = sslContext.getServerSocketFactory();
		SSLServerSocket serverSocket = (SSLServerSocket) factory
				.createServerSocket(thePort);

		serverSocket.setUseClientMode(false);
		serverSocket.setNeedClientAuth(true);

		serverSocket.setSoTimeout(SERVER_ACCEPT_TIMEOUT_TIME);
		return serverSocket;
	}
}

// ** 待改进



// ** 说明

// 监听线程采用单线程处理，可避免同时增加多个刷卡机连接时到并发性问题

// SSL监听线程采用同步方式，若有一个刷卡机卡在SSL握手，其他刷卡机将等待

// SSL握手有时候会阻塞，在监听线程中加入超时定时器，若监听线程卡住则不能及时更新状态，将由主循环线程重启
// // 有时刷卡机端会更早的超时重连，然后重连还会阻塞，过一段时间重连几次问题会自动消除
// // 出现比较随机，概率比较小

